package com.ld.userpay.service.handler.impl;

import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradePrecreateModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.reflect.ClassUtil;
import com.ld.shieldsb.common.core.util.ResultUtil;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.common.core.util.date.DateUtil;
import com.ld.shieldsb.common.web.util.Web;
import com.ld.shieldsb.common.web.util.WebUtil;
import com.ld.userpay.model.OrderModel;
import com.ld.userpay.model.config.AliPayBean;
import com.ld.userpay.service.IOrderService;
import com.ld.userpay.service.handler.IPayHandler;
import com.ld.userpay.util.pay.PayUtil;
import com.ld.userpay.util.pay.PayUtil.PayType;

import lombok.extern.slf4j.Slf4j;

/**
 * 微信支付处理器
 * 
 * @ClassName WxPayHandler
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2022年4月25日 下午4:11:20
 *
 */
@Slf4j
@Service
public class AliPayHandler implements IPayHandler {
    @Resource
    protected AliPayBean aliPayBean; // 支付宝支付参数
    @Autowired
    protected IOrderService chargeService; // 支付服务类，需要在具体项目中实现

    public AliPayApiConfig getApiConfig() throws AlipayApiException {
        AliPayApiConfig aliPayApiConfig;
        try {
            aliPayApiConfig = AliPayApiConfigKit.getApiConfig(aliPayBean.getAppId());
        } catch (Exception e) {
            aliPayApiConfig = AliPayApiConfig.builder().setAppId(aliPayBean.getAppId()).setAliPayPublicKey(aliPayBean.getPublicKey())
                    .setAppCertPath(aliPayBean.getAppCertPath()).setAliPayCertPath(aliPayBean.getAliPayCertPath())
                    .setAliPayRootCertPath(aliPayBean.getAliPayRootCertPath()).setCharset("UTF-8").setPrivateKey(aliPayBean.getPrivateKey())
                    .setServiceUrl(aliPayBean.getServerUrl()).setSignType("RSA2");
            if (StringUtils.isNotEmpty(aliPayBean.getAliPayCertPath())) {
                // 证书模式
                aliPayApiConfig = aliPayApiConfig.buildByCert();
            } else {
                // 普通公钥方式
                aliPayApiConfig = aliPayApiConfig.build();

            }

        }
        return aliPayApiConfig;
    }

    public void initAliPay() throws AlipayApiException {
        // 设置到当前线程中
        AliPayApiConfigKit.setThreadLocalAliPayApiConfig(getApiConfig());
    }

    @Override
    public Result getCodeUrl(HttpServletRequest request, HttpServletResponse response, String orderid, String body, String attach) {

        String subject = body;

        // 查询订单钱数，注意这个是需要根据订单号查询的，不能直接传值，可能被篡改
        OrderModel financeModel = chargeService.getOrderInfo(orderid);
        // 钱数单位是分
        Double payout = financeModel.getPayMoney(); // 这个钱数单位是元
        // 注意调用工具类获取实际支付钱数
        Double payoutFen = PayUtil.getPayMoney(payout, request);
        // 需要转成字符
        String totalAmount = payoutFen.intValue() + "";

        String notifyUrl = aliPayBean.getDomain().concat(getNotifyUrl());

        AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
        model.setSubject(subject);
        model.setTotalAmount(totalAmount);
        model.setTimeoutExpress("5m");
        model.setOutTradeNo(orderid);
        try {
            String resultStr = AliPayApi.tradePrecreatePayToResponse(model, notifyUrl).getBody();
            JSONObject jsonObject = JSONObject.parseObject(resultStr);
            String qrCodeUrl = jsonObject.getJSONObject("alipay_trade_precreate_response").getString("qr_code");
            return ResultUtil.success("", qrCodeUrl);
        } catch (Exception e) {
            log.error("", e);
            return ResultUtil.error("错误", e);
        }
    }

    @Override
    public PayType type() {
        return PayType.ALIPAY;
    }

    @Override
    public Result pcPay(HttpServletRequest request, HttpServletResponse response, String orderid, String subject, String body) {
        try {
            // 初始化参数
            initAliPay();
            String passBackParams = "passback_params"; // 自定义参数

            String outTradeNo = orderid;
            log.debug("pc outTradeNo>" + outTradeNo);

            String returnUrl = aliPayBean.getDomain().concat(getReturnUrl()); // 同步
            String notifyUrl = aliPayBean.getDomain().concat(getNotifyUrl()); // 异步通知

            // 查询订单钱数，注意这个是需要根据订单号查询的，不能直接传值，可能被篡改
            OrderModel financeModel = chargeService.getOrderInfo(orderid);
            // 钱数单位是分
            Double payout = financeModel.getPayMoney(); // 这个钱数单位是元
            // 注意调用工具类获取实际支付钱数
            Double payoutReal = PayUtil.getPayMoney(payout, request);
            // 需要转成整数
            String money = payoutReal + "";

            AlipayTradePagePayModel model = new AlipayTradePagePayModel();

            model.setOutTradeNo(outTradeNo);
            model.setProductCode("FAST_INSTANT_TRADE_PAY");
            model.setTotalAmount(money);
            model.setSubject(subject);
            model.setBody(body);
            model.setPassbackParams(passBackParams);
            /**
             * 花呗分期相关的设置,测试环境不支持花呗分期的测试 hb_fq_num代表花呗分期数，仅支持传入3、6、12，其他期数暂不支持，传入会报错；
             * hb_fq_seller_percent代表卖家承担收费比例，商家承担手续费传入100，用户承担手续费传入0，仅支持传入100、0两种，其他比例暂不支持，传入会报错。
             */
//            ExtendParams extendParams = new ExtendParams();
//            extendParams.setHbFqNum("3");
//            extendParams.setHbFqSellerPercent("0");
//            model.setExtendParams(extendParams);

            AliPayApi.tradePage(response, model, notifyUrl, returnUrl);
        } catch (Exception e) {
            log.warn("", e);
        }
        return ResultUtil.success("");
    }

    @Override
    public Result wapPay(HttpServletRequest request, HttpServletResponse response, String orderid, String subject, String body) {

        try {
            // 初始化参数
            initAliPay();
            String passBackParams = "passback_params"; // 自定义参数

            String returnUrl = aliPayBean.getDomain().concat(getReturnUrl()); // 同步
            String notifyUrl = aliPayBean.getDomain().concat(getNotifyUrl()); // 异步通知

            // 查询订单钱数，注意这个是需要根据订单号查询的，不能直接传值，可能被篡改
            OrderModel financeModel = chargeService.getOrderInfo(orderid);
            // 钱数单位是分
            Double payout = financeModel.getPayMoney(); // 这个钱数单位是元
            // 注意调用工具类获取实际支付钱数
            Double payoutReal = PayUtil.getPayMoney(payout, request);
            // 需要转成整数
            String money = payoutReal + "";

            AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
            model.setBody(body);
            model.setSubject(subject);
            model.setTotalAmount(money);
            model.setPassbackParams(passBackParams);
            model.setOutTradeNo(orderid);
            model.setProductCode("QUICK_WAP_PAY");
            AliPayApi.wapPay(response, model, returnUrl, notifyUrl);
        } catch (Exception e) {
            log.warn("", e);
        }
        return ResultUtil.success("");
    }

    /**
     * 异步通知
     * 
     * @Title payNotify
     * @author 吕凯
     * @date 2022年4月24日 上午11:15:54
     * @param request
     * @param response
     * @param type
     *            void
     */
    @Override
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        // 支付宝，POST
        // 参考文档 https://opendocs.alipay.com/support/01raw4
        // 收到异步通知后，商家输出 success 表示消息获取成功，支付宝就会停止发送异步，如果输出 fail，表示消息获取失败，支付宝会重新发送消息到异步地址。
        // 建议在接收异步进行验签，如果验签成功输出 success，验签失败返回 fail，重新接收异步进行处理。
        String flag = "fail";
        try {
            // 获取支付宝GET过来反馈信息
            Map<String, String> params = AliPayApi.toMap(request);
            log.debug("支付通知{} params：{}", params);
            /*map {gmt_create=2022-04-23 09:28:48, charset=UTF-8, subject=工美平台微信支付, 
             sign=MUPVzxlwUYdjPeegKr3AZAt7w8GKWKxh553TqdQfXS7+JtDMWv2iflKEFUOkQ4CSgfgZLvY3P/U6yPyIMb5UJuo5OXQudiL8N+mn+
             I66TIMaAPrgWZbfwwZZX0bEfSii5pKgEMBnn/sXv7NoU1+w0BvualW78gX5Qlk7nPbQcGUSAt1tavHzsBCq4vJEZgUBwStqLA3bi2La2DVYuJm/u/
             YGEVhkzldF0KFruh1Yp6upLV3EKczXQkmXXCk3461Jdowy8KHb0TodUrXFpnj9HiO5vAMoi3Qs9RkPPYojEJ7syI5JtyNeFAvw6D+gwwal8xK/epxv/
             JdjsAiyBiIOVw==, buyer_id=2088002516613914, body=支付测试, invoice_amount=0.01, notify_id=2022042300222092859013911413407171, 
             fund_bill_list=[{"amount":"0.01","fundChannel":"PCREDIT"}], notify_type=trade_status_sync, trade_status=TRADE_SUCCESS, 
             receipt_amount=0.01, buyer_pay_amount=0.01, app_id=2021003124648277, sign_type=RSA2, seller_id=2088802914452447, 
             gmt_payment=2022-04-23 09:28:58, notify_time=2022-04-23 09:32:23, passback_params=passback_params, version=1.0, 
             out_trade_no=2fb09880725d492dba8d4ec38aa182ad, total_amount=0.01, trade_no=2022042322001413911445524605, 
             auth_app_id=2021003124648277, point_amount=0.00}*/

            /*WAIT_BUYER_PAY ：交易创建，等待买家付款。
            TRADE_CLOSED ：在指定时间段内未支付时关闭的交易或在交易完成全额退款成功时关闭的交易。
            TRADE_SUCCESS：商户签约的产品支持退款功能的前提下，买家付款成功。
            TRADE_FINISHED：商户签约的产品不支持退款功能的前提下，买家付款成功；或者，商户签约的产品支持退款功能的前提下，交易已经成功并且已经超过可退款期限。*/
            String returnCode = params.get("trade_status"); //
            String outTradeNo = params.get("out_trade_no");
            String transactionId = params.get("trade_no");
            String totalFeeStr = params.get("total_amount"); // 这里需要注意，因为又多个钱数，buyer_pay_amount可能不等于真实钱数，有优惠券之类的
            String paymentTime = params.get("gmt_payment"); // 支付时间

            boolean verifyResult = AlipaySignature.rsaCheckV1(params, aliPayBean.getPublicKey(), "UTF-8", "RSA2");

            if (verifyResult) { // return_url 验证成功
                if ("TRADE_SUCCESS".equals(returnCode)) {

                    Double totalFee = ClassUtil.obj2doulbe(totalFeeStr, null); // 元
                    // 回调处理，订单状态更新以及其他的业务逻辑处理
                    Result result = chargeService.payNotifyCallback(true, outTradeNo, transactionId, PayUtil.PayType.ALIPAY.getKeyword(),
                            totalFee, DateUtil.str2dateTime(paymentTime), WebUtil.getIpAddr(request));
                    if (result.getSuccess()) {
                        flag = "success";
                    }
                }
            }
        } catch (Exception e) {
            log.error("", e);
        }
        Web.Response.write(response, flag); // 直接返回字符串会加双引号，检验不通过
    }

    /**
     * 同步通知, session可取到支付人的session信息，与异步的处理可以不同
     */
    public Pair<Result, Map<String, String>> payReturn(HttpServletRequest request, HttpServletResponse response) {
        // GET
        request.setAttribute("payType", "支付宝");
        // 参考文档 https://opendocs.alipay.com/support/01raw3

        try {

            // 获取支付宝GET过来反馈信息
            Map<String, String> params = AliPayApi.toMap(request);
            log.warn("支付通知{} params：{}", params);
            /*支付通知alipay params：
             * 
             * {charset=UTF-8, out_trade_no=abc183df86884cad870f78efae57be17, method=alipay.trade.page.pay.return, total_amount=0.01, 
             sign=MrFD1EFe8r7UlCz3830Ok6tCH38flqX0aeIUzxayiBe0D1UCd0L1CWaM0k6gwJWf/1f0Tmj6hn3WuM3WyGAR5ut7vPAage61q8TxBtukYYWic3ax91y8JZG
             /47R3al6akVvHr8/QGkaxWTrg/e4UwFV6ylYDarh9H3OpXQJohNcITgZ9W7KVQjA5vXr3DaDEbj8KE+N3pH6vzaBtrIe/h55MFjjNBres66XsMNehlV9f3RBL0HK
             6gcCyS1P7jymvteZU142EuAPAkQWWi2m5G1jHt87JFnT3gj9LxlXp1LdjalTp/qHfLWNoKTpZtN4XeyiLwJEjFf1AxqWmRbr2Tw==, 
             trade_no=2022042322001413911445754734, auth_app_id=2021003124648277, version=1.0, app_id=2021003124648277, sign_type=RSA2, 
              seller_id=2088802914452447, timestamp=2022-04-23 13:02:03}   com.ld.admin.user.controller.usercenter.UserChargeNotifyController
              .returnNotify(UserChargeNotifyController.java:167) */

            boolean verifyResult = AlipaySignature.rsaCheckV1(params, aliPayBean.getPublicKey(), "UTF-8", "RSA2");

            if (verifyResult) { // return_url 验证成功
                String outTradeNo = params.get("out_trade_no"); // 外部订单号
                String transactionId = params.get("trade_no"); // 购买渠道订单号
                String totalFeeStr = params.get("total_amount"); // 支付金额
                String orderTime = params.get("timestamp"); // 当前时间戳

                // 同步通知不返回状态码，所以不处理订单
                // Double totalFee = ClassUtil.obj2doulbe(totalFeeStr, null); // 元
                // updatePayAfterInfo(outTradeNo, transactionId, PayStaticParams.PAY_TYPE_ALIPAY, totalFee);

                request.setAttribute("orderId", outTradeNo);
                request.setAttribute("orderInnerId", transactionId);
                request.setAttribute("orderAmount", totalFeeStr);
                request.setAttribute("orderTime", orderTime);
                return Pair.of(ResultUtil.success(""), params);
            }
            return Pair.of(ResultUtil.error("验证失败！"), params);
        } catch (Exception e) {
            log.error("", e);
        }
        return Pair.of(ResultUtil.error("无效的支付渠道"), null);
    }

    /**
     * 退款，如果报ALIPAY_CERT_SN is Empty，检查是不是使用了证书模式，参考https://opensupport.alipay.com/support/helpcenter/287/201602622795#
     * 
     * @Title refund
     * @author 吕凯
     * @date 2022年4月26日 下午5:03:23
     * @param outTradeNo
     * @param tradeNo
     * @param monny
     * @return
     * @see com.ld.userpay.service.handler.IPayHandler#refund(java.lang.String, java.lang.String, java.lang.Double)
     */
    @Override
    public Result refund(HttpServletRequest request, HttpServletResponse response, String outTradeNo, String tradeNo, Double monny) {
        try {
            // 初始化参数
            initAliPay();
            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
            if (StringUtils.isNotEmpty(outTradeNo)) {
                model.setOutTradeNo(outTradeNo);
            }
            if (StringUtils.isNotEmpty(tradeNo)) {
                model.setTradeNo(tradeNo);
            }
            model.setRefundAmount(monny + "");
            model.setRefundReason("正常退款");
            String resultStr = AliPayApi.tradeRefundToResponse(model).getBody();
            JSONObject json = JSONObject.parseObject(resultStr);
            log.debug("resultStr {}", resultStr);
            JSONObject responseObj = json.getJSONObject("alipay_trade_refund_response");
            String code = responseObj.getString("code");
            String totalFeeStr = json.getString("refund_fee");
            String paymentTime = json.getString("gmt_refund_pay"); // 支付时间

//            String msg = response.getString("msg");
            if ("10000".equals(code)) {
                Double totalFee = ClassUtil.obj2doulbe(totalFeeStr, null); // 元
                chargeService.refundCallback(true, outTradeNo, tradeNo, PayUtil.PayType.ALIPAY.getKeyword(), totalFee,
                        DateUtil.str2dateTime(paymentTime), WebUtil.getIpAddr(request));
                return ResultUtil.success("", json);
            }
            /* {"alipay_trade_refund_response":{"code":"10000","msg":"Success","buyer_logon_id":"253***@qq.com",
             "buyer_user_id":"2088002516613914","fund_change":"N","gmt_refund_pay":"2022-04-27 10:03:57",
             "out_trade_no":"GM20220427095524933584","refund_fee":"0.01","send_back_fee":"0.00",
             "trade_no":"2022042722001413911449400352"},"sign":"RLp9f+L3JING+6YqS8BEZzA0aTDSPLOhNCsO5/KXYT0jFWhpHbJg3Hbmu
             v47K9ZaJxS7DrlwcGmcZ7S/PImEMJODqc0yGI3O+uCuGSaiCRQRl0HV+uqYhvkqS4syerZRDqr1IrJkwTv/GyvmKyiiF0EtfroMWNmYqNYIk
             qlPTvffzuCouF+QQyuc7TWFO7cz6D/FxKadnJTmAg0IdlNI+2zPkPd63aQQnHaVWXLuSZxuBxnBPSb0VMgf4ahoRBlANdVm8Pg2X86wo7unk
             xFXwG+lSiF8iEzf6AMi3rLujRUI1XiJT9DiFwIqVP+zDkZwEXt/gkUL1xO4TGw+v3vwrA=="}  */
            return ResultUtil.error("退款失败！", json);
        } catch (AlipayApiException e) {
            log.error("退款失败！", e);
        }
        return ResultUtil.error("退款失败！");
    }

}
